<?php
include_once("class/Atelier.php");
include_once("class/Client.php");
include_once("class/Commande.php");
include_once("class/Contrat.php");
include_once("class/ParamEntreprise.php");

// lacreuse@unistra.fr

// Initialise le traitement
function init($augmentation = false)
{
    // Paramétre entreprise
    $params = new ParamEntreprise("01-10-1969");
    
    if($augmentation)
    {
        $pourcentage_augmentation = 1 + $params->getAugmentationPrevisionnelleCommande();
    }
    else
    {
        $pourcentage_augmentation = 1;
    }
    
    // Les ateliers
    $machine_critique = new Atelier(1, "Forgeage", 6, 2, true, null);
    
    $machines = array(4);
    $machines[0] = new Atelier(0, "Découpe", 2, 1, false, $machine_critique);
    $machines[1] = $machine_critique;
    $machines[2] = new Atelier(2, "Traitement de surface", 2, 3, false, $machine_critique);
    $machines[3] = new Atelier(3, "Emballage", 2, 4, false, $machine_critique);
    
    // Le client
    $client_alztom = new Client(0, "ALZTOM", "Pont de Millaud");
    $client_tomy = new Client(1, "TOMY", "Pont de Normandie");
    
    // Le contrat signé pour chaque société
    $contrat_alztom = new Contrat(0, $client_alztom, 2);
    $contrat_tomy = new Contrat(1, $client_tomy, 3);
    
    // La commande pour la société ALZTOM
    $commandes = array();
    $commandes[] = new Commande(0, "Boulons", (15000*$pourcentage_augmentation), "15-11-1969", $contrat_alztom);
    $commandes[] = new Commande(1, "Boulons", (15000*$pourcentage_augmentation), "01-12-1969", $contrat_alztom);
    $commandes[] = new Commande(2, "Boulons", (15000*$pourcentage_augmentation), "15-12-1969", $contrat_alztom);
    $commandes[] = new Commande(3, "Boulons", (15000*$pourcentage_augmentation), "01-01-1970", $contrat_alztom);
    $commandes[] = new Commande(4, "Boulons", (15000*$pourcentage_augmentation), "15-01-1970", $contrat_alztom);
    $commandes[] = new Commande(5, "Boulons", (15000*$pourcentage_augmentation), "01-02-1970", $contrat_alztom);
    
    // La commande pour la société TOMY
    $commandes[] = new Commande(6, "Boulons", (5000*$pourcentage_augmentation), "01-02-1970", $contrat_tomy);
    $commandes[] = new Commande(7, "Boulons", (5000*$pourcentage_augmentation), "05-02-1970", $contrat_tomy);
    $commandes[] = new Commande(8, "Boulons", (5000*$pourcentage_augmentation), "10-02-1970", $contrat_tomy);
    $commandes[] = new Commande(9, "Boulons", (5000*$pourcentage_augmentation), "15-02-1970", $contrat_tomy);
    $commandes[] = new Commande(10, "Boulons", (5000*$pourcentage_augmentation), "20-02-1970", $contrat_tomy);
    $commandes[] = new Commande(11, "Boulons", (5000*$pourcentage_augmentation), "25-02-1970", $contrat_tomy);
    
    // Tableau des résultats !
    $resultat = array();
    $resultat[] = array("Date",
        "Produits fabriqués (boulons)",
        "Produits en stock (boulons)",
        "Bobines en stock",
        "Coût horaire de l'usine",
        "Coût des bobines",
        "Nombre de bobines achetées");
    
    $commande_bobine = array();
    $commande_bobine[] = array("Date de la commande",
        "Date d'arrivée",
        'qte' => "Quantité",
        'prix' => "Prix par bobine");
    
    $commande_expedie = array();
    
    $affiche_commande_expedie = array();
    $affiche_commande_expedie[] = array(
        "ID",
        "Nom Contrat",
        "Date de départ",
        "Date d'arrivée",
        "Delai",
        "Date de livraison souhaitée",
        "Quantité",
        "Prix de la commande",
        "Prix par boulon");
    
    // Résultat initial
    $resultat[] = array('Initiale',
        formater($params->getNombreProduitProduit()),
        formater($params->getNombreProduitEnStock()),
        $params->getStockBobineAjd(),
        formater($params->getCoutHoraireTotal(), 2) .'€',
        formater($params->getCoutBobineTotal(), 2) .'€',
        $params->getNombreBobineAchete());
    
    $commande_restante = true;
    $mois_precedent = null;
    // Boucle tant que toutes les commandes ne sont pas livré
    while($commande_restante)
    {
        $time = getTimeOfDate($params->getDateFin());
        $mois_en_cours = date("m", $time);
        
        if(empty($mois_precedent))
        {
            $mois_precedent = $mois_en_cours;
        }
        else if($mois_precedent != $mois_en_cours)
        {
            // Inflation du prix de la bobine
            $prix_bobine = $params->getPrixBobineAjd();
            $inflation = 1+$params->getInflationAcier();
            
            $nouveau_prix = round($prix_bobine*$inflation, 2);
            $params->setPrixBobineAjd($nouveau_prix);
            $mois_precedent = $mois_en_cours;
        }
        
        // Recalcule le nombre de commande passé et leur cout
        $nombre = 0;
        $cout = 0;
        
        // Pour les commandes de bobines
        foreach ($commande_bobine as $cmd)
        {
            // Si la date correspond à la livraison
            if(isset($cmd['date_livraison']) and getDateOfTime($cmd['date_livraison']) == getDateOfTime($time))
            {
                // Ajoute la quantité commandé
                $nbbobine = $params->getStockBobineAjd() + $cmd['qte'];
                while($nbbobine > 2)
                {
                    $nbbobine--;
                }
                $params->setStockBobineAjd($nbbobine);
                // Enregistre le résultat par jour
                $resultat[] = array('warning',
                    date("l", $time) .' '. getDateOfTime($time),
                    '-',
                    '-',
                    '+'.$cmd['qte'],
                    '-',
                    '-',
                    '-');
            }
            
            $nombre += $cmd['qte'];
            $cout += $cmd['prix'] * $cmd['qte'];
        }
        // Met à jour le nombre de bobine acheté
        $params->setNombreBobineAchete($nombre);
        // Met à jour le cout d'achat des bobines
        $params->setCoutBobineTotal($cout);
        
        // Uniquement les jours ouvrés
        if(estOuvre($time))
        {
            // Ajoute un jour travaillé
            $params->addNombreJoursTravaillesTotal(1);
            
            // Production par heure : 7 h / jour
            for($i=0; $i<7; $i++)
            {
                foreach($machines as $m)
                {
                    // Identifiant de la machine
                    $id = $m->getIdAtelier();
                    
                    if(!$m->getCritique() and $m->ordreInfCritique())
                    {
                        if($m->getNombreHeurePasse() == $m->getDureeAtelier())
                        {
                            $m->addProduit();
                            if($id > 0)
                            {
                                // Vide le stock précédent
                                $machines[$id-1]->delProduit($m->getNombreProduitSortie());
                                // Si on se trouve sur la dernière machine
                                if($id == 3)
                                {
                                    $params->addNombreProduitProduit($m->getNombreProduitSortie());
                                }
                            }
                        }
                        // Si le nb heure passé == temps critique alors on relance la prod.
                        if($m->getNombreHeurePasse() == $m->getDureeMachineCritique())
                        {
                            $m->setNombreHeurePasse(0);
                        }
                    }
                    else
                    {
                        if($m->getNombreHeurePasse() == $m->getDureeAtelier())
                        {
                            $m->addProduit();
                            $m->setNombreHeurePasse(0);
                            if($id > 0)
                            {
                                // Vide le stock précédent
                                $machines[$id-1]->delProduit($m->getNombreProduitSortie());
                                // Si on se trouve sur la dernière machine
                                if($id == 3)
                                {
                                    $params->addNombreProduitProduit($m->getNombreProduitSortie());
                                }
                            }
                        }
                    }
                    
                    // Attend qu'il y ai suffisament de produit précédent
                    if($m->ordreInfCritique() or $machines[$id-1]->getNombreProduitFini() >= $m->getNombreProduitSortie())
                    {
                        // Si ce n'est pas la 1er machine ou qu'il y des bobines en stock
                        if($id > 0 or $params->getStockBobineAjd() > 0)
                        {
                            // Lancement de la production
                            $m->addHeure();
                        
                            // Retire une bobine lorsque la 1er machine s'est lancé
                            if($id == 0 and $m->getNombreHeurePasse() == 1)
                            {
                                $nbbobine = $params->getStockBobineAjd();
                                $params->setStockBobineAjd($nbbobine - 1);
                            }
                        }
                        else
                        {      
                            // Enregistre le résultat par jour
                            $resultat[] = array('info', date("l", $time) .' '. getDateOfTime($time) .' '. ($i + 1) .'h',
                                formater($params->getNombreProduitProduit()),
                                formater($params->getNombreProduitEnStock()),
                                $params->getStockBobineAjd(),
                                formater($params->getCoutHoraireTotal(), 2) .'€',
                                formater($params->getCoutBobineTotal(), 2) .'€',
                                $params->getNombreBobineAchete());
                        }
                    }
                    
                    // Enregistre le résultat par heure
//                    $resultat[] = array(' Heure '. ($i + 1) .' ; '.$m->getNomAtelier(),
//                        $m->getNombreProduitFini(),
//                        $params->getStockBobineAjd());
                }
                
                // Pour chaque heure de fonctionnement ajoute le cout d'usine
                $params->addCoutHoraireTotal($params->getCoutHoraireUsine());
            }
            
            // Met à jour la date de la livraison d'une bobine (Jour + Délai + 4)
            $time_livraison = $time + (60*60*24*$params->getTempsLivraisonBobine()) + (60*60*24*4);

            $compte_commande_bobine = count($commande_bobine) - 1;
            if(($compte_commande_bobine % 6) == 0)
            {
                $quantite = 2;
            }
            else
            {
                $quantite = 1;
            }

            // $quantite = $params->getStockMaxBobine() - $params->getStockBobineAjd();
            $prix_bobine = $params->getPrixBobineAjd();
            // Commande uniquement si le jour de livraison est un jour ouvré
            while(!estOuvre($time_livraison))
            {
                $time_livraison += 24*60*60;
            }

            // Commande uniquement si le jour de livraison est un jour ouvré
            if(estOuvre($time_livraison))
            {
                $commande_bobine[] = array('date_commande' => $time,
                    'date_livraison' => $time_livraison,
                    'prix' => $prix_bobine,
                    'libelle_date_commande' => date("l", $time) .' '. getDateOfTime($time),
                    'libelle_date_livraison' => date("l", $time_livraison) .' '. getDateOfTime($time_livraison),
                    'qte' => formater($quantite),
                    'libelle_prix' => formater($prix_bobine, 2) .'€');
            }
            
            // Augmentation du stock / jour
            $params->setNombreProduitEnStock($machines[3]->getNombreProduitFini());
            
            // Enregistre le résultat par jour
            $resultat[] = array(date("l", $time) .' '. getDateOfTime($time),
                formater($params->getNombreProduitProduit()),
                formater($params->getNombreProduitEnStock()),
                $params->getStockBobineAjd(),
                formater($params->getCoutHoraireTotal(), 2) .'€',
                formater($params->getCoutBobineTotal(), 2) .'€',
                $params->getNombreBobineAchete());
        }
        else
        {
            // Enregistre les jours non ouvré
            $resultat[] = array('error', date("l", $time) .' '. getDateOfTime($time),
                formater($params->getNombreProduitProduit()),
                formater($params->getNombreProduitEnStock()),
                $params->getStockBobineAjd(),
                formater($params->getCoutHoraireTotal(), 2) .'€',
                formater($params->getCoutBobineTotal(), 2) .'€',
                $params->getNombreBobineAchete());
        }
        
        // Suppose que toutes les commandes ont été expédié
        $commande_restante = false;
        // Départ des commandes
        foreach ($commandes as $cmd)
        {
            // Si la commande n'est pas encore expedié
            if(!$cmd->getExpedie())
            {
                // Si au moins une commande reste non expédié
                $commande_restante = true;
                
                // Date de livraison souhaité par le client
                $date_livraison = $cmd->getDateLivraison();
                // Délai de livraison
                $contrat = $cmd->getContrat();
                $delai_livraison = $contrat->getDelaiLivraison();
                // Date de départ idéal
                $time_depart = getTimeOfDate($date_livraison) - (60*60*24*$delai_livraison);
                
                while(!estOuvre($time_depart))
                {
                    // Recule le départ d'un jour
                    $time_depart -= 60*60*24;
                }
                // Date d'arrivée après le départ
                $time_arrivee = $time + (60*60*24*$delai_livraison);
                
                // Si la date correspond ou que la commande est retardé
                if($time_depart == $time or $cmd->getRetard())
                {
                    // Expédie la commande
                    $quantite_expedie = $cmd->getQuantite();
                    $quantite_stock = $machines[3]->getNombreProduitFini();
                    
                    // Verifie que la quantité en stock est suffisante
                    if($quantite_stock >= $quantite_expedie)
                    {
                        // Calcul du prix
                        $nombre_bobine_necessaire = round($quantite_expedie / $params->getNombreProduitParBobbine());
                        // Nombre d'heure pour produire 1000 boulons
                        $nombre_heure_necessaire = $machine_critique->getDureeMachineCritique();
                        // Nombre d'heure pour réaliser la commande (au maximum)
                        $cout_horaire_commande = $nombre_bobine_necessaire * $nombre_heure_necessaire * $params->getCoutHoraireUsine();
                        // Cout des dernière bobines
                        $i = 0;
                        $j = count($commande_bobine) - 1;
                        $cout_bobine_commande = 0;
                        while($i < $nombre_bobine_necessaire)
                        {
                            $i += $commande_bobine[$j]['qte'];
                            // Si le nombre de bobine necessaire est dépassé
                            if($i > $nombre_bobine_necessaire)
                            {
                                $qte = 1;
                            }
                            else
                            {
                                $qte = $commande_bobine[$j]['qte'];
                            }
                            $cout_bobine_commande += ($qte * $commande_bobine[$j]['prix']);
                            
                            $j--;
                        }
                        
                        // Met à jour le nombre de produit fini
                        $machines[3]->setNombreProduitFini($quantite_stock - $quantite_expedie);
                        // Confirme l'envoi de la commande
                        $cmd->setExpedie(true);
                        // Met à jour la date de départ
                        $cmd->setDateDepart($time);
                        // Met à jour la date d'arrivee
                        $cmd->setDateArrivee($time_arrivee);
                        
                        // Taux
                        $taux_marge = $params->getMargeSurCoutRevient();
                        // Prix par commande
                        $cout_usine = $cout_horaire_commande;
                        $cout_bobine = $cout_bobine_commande;
                        $cout_revient = $cout_usine + $cout_bobine;
                        
//                        $prix_vente = $cout_revient / (1 - $taux_marge);
//                        $marge = $prix_vente - $cout_revient;
                        
                        $marge = round($cout_revient * $taux_marge, 2);
                        $prix_vente = round($cout_revient + $marge, 2);
                        $prix_par_boulon = $prix_vente / $cmd->getQuantite();
                        
                        // Enregistre le prix du boulon
                        $cmd->setPrix($prix_par_boulon);
                        
                        // Enregistre l'information
                        $commande_expedie[] = array(
                            'id' => $cmd->getIdCommande(),
                            'contrat' => $contrat->getNomClient(),
                            'depart' => $time,
                            'arrivee' => $time_arrivee,
                            'delai' => $delai_livraison,
                            'livraison' => $date_livraison,
                            'qte' => $cmd->getQuantite(),
                            'prix' => $cmd->getPrix(),
                            'prix_vente' => $prix_vente,
                            'retard' => $cmd->getRetard());
                        
                        // Diminution du stock / jour
                        $params->setNombreProduitEnStock($machines[3]->getNombreProduitFini());
                        // Enregistre le résultat par jour
                        $resultat[] = array('success',
                            date("l", $time) .' '. getDateOfTime($time),
                            '-',
                            '-'.formater($quantite_expedie),
                            '-',
                            '-',
                            '-',
                            '-');
                    }
                    else
                    {
                        // Reporte l'envoi de la commande
                        $cmd->setRetard(true);
                    }
                }
            }
        }
        
        // +Plus+ 1 jour
        $time += 60 * 60 * 24;
        $params->setDateFin(getDateOfTime($time));
    }
    
    // Affichage des commandes initiales
    $affiche_commandes = array();
    $affiche_commandes[] = array(
        "Identifiant de la commande",
        "Nom du client",
        "Produit souhaité",
        "Quantité",
        "Date de livraison");
    
    // Pour les commandes
    foreach($commandes as $cmd)
    {
        $affiche_commandes[] = array($cmd->getIdCommande(),
            $cmd->getContrat()->getNomClient(),
            $cmd->getLibelleProduit(),
            formater($cmd->getQuantite()),
            $cmd->getDateLivraison());
    }
    
    $gain_par_projet = array();
    
    $gain_total = array(
        'prix_total' => 0,
        'prix_unitaire' => 0,
        'nombre' => 0,
        'qte_total' => 0
    );
    
    // Pour les commandes expediées
    foreach ($commande_expedie as $cmd)
    {
        $class = '';
        $livraison = $cmd['livraison'];
        if($cmd['retard'])
        {
            $class = 'error';
            $time_liv = new DateTime(date("Y-m-d", getTimeOfDate($cmd['livraison'])));
            $time_arr = new DateTime(date("Y-m-d", $cmd['arrivee']));
            $diff = date_diff($time_liv, $time_arr);
            $livraison .= ' ('. $diff->format("%a") .' jours de retards)';
        }
        
        $affiche_commande_expedie[] = array($class,
            $cmd['id'],
            $cmd['contrat'],
            getDateOfTime($cmd['depart']),
            getDateOfTime($cmd['arrivee']),
            $cmd['delai'],
            $livraison,
            formater($cmd['qte']),
            formater($cmd['prix'] * $cmd['qte'], 2) .'€',
            formater($cmd['prix'], 2) .'€');
        
        // Gain sur chaque contrat
        if(!isset($gain_par_projet[$cmd['contrat']]))
        {
            $gain_par_projet[$cmd['contrat']] = array(
                'prix_total' => 0,
                'prix_unitaire' => 0,
                'nombre' => 0,
                'qte_total' => 0
            );
        }
        $gain_par_projet[$cmd['contrat']]['prix_total'] += $cmd['prix'] * $cmd['qte'];
        $gain_par_projet[$cmd['contrat']]['prix_unitaire'] += $cmd['prix'];
        $gain_par_projet[$cmd['contrat']]['nombre']++;
        $gain_par_projet[$cmd['contrat']]['qte_total'] += $cmd['qte'];
        
        $gain_total['prix_total'] += $cmd['prix'] * $cmd['qte'];
        $gain_total['prix_unitaire'] += $cmd['prix'];
        $gain_total['nombre']++;
        $gain_total['qte_total'] += $cmd['qte'];
    }
    $gain_par_projet['TOTAL'] = $gain_total;
    
    $affiche_gain_par_projet = array();
    $affiche_gain_par_projet[] = array(
        "Contrat",
        "Quantité vendue",
        "Prix de vente",
        "Prix unitaire moyen");
    
    foreach ($gain_par_projet as $key => $value)
    {
        $class = '';
        if($key == 'TOTAL')
        {
            $class = 'success';
        }
        
        $prix_total = $value['prix_total'];
        $prix_unitaire = $value['prix_unitaire'] / $value['nombre'];
        $qte_total = $value['qte_total'];
        
        $affiche_gain_par_projet[] = array($class,
            $key,
            formater($qte_total),
            formater($prix_total, 2) .'€',
            formater($prix_unitaire, 2) .'€'
            );
    }
    
    // Affichage des contrat initiales
    $affiche_projet = array();
    $affiche_projet[] = array(
        "Identifiant du projet",
        "Nom du client",
        "Délai de livraison");
    $affiche_projet[] = array($contrat_alztom->getIdContrat(),
        $contrat_alztom->getNomClient(),
        $contrat_alztom->getDelaiLivraison());
    $affiche_projet[] = array($contrat_tomy->getIdContrat(),
        $contrat_tomy->getNomClient(),
        $contrat_tomy->getDelaiLivraison());
    
    // Résultat
    $cout_usine = $params->getCoutHoraireTotal();
    $cout_bobine = $params->getCoutBobineTotal();
    $cout_revient = $cout_usine + $cout_bobine;
//    $taux_marge = $params->getMargeSurCoutRevient();
//    $marge = round($cout_revient * $taux_marge, 2);
//    $prix_vente = round($cout_revient + $marge, 2);
//    $prix_par_boulon = round($prix_vente / $params->getNombreProduitProduit(), 2);
    $benefice = $gain_total['prix_total'] - $cout_revient;
    $taux_marge_reel = $benefice / $cout_revient;
    
//    $prix_ventes = array();
//    $prix_ventes[] = array(
//        "Cout de l'usine",
//        "Cout des bobines",
//        "Cout de revient",
//        "Taux de marge",
//        "Marge",
//        "Prix de vente global estimé",
//        "Prix par boulon"
//        );
//    $prix_ventes[] = array(formater($cout_usine, 2) .'€',
//        formater($cout_bobine, 2) .'€',
//        formater($cout_revient, 2) .'€',
//        ($taux_marge * 100) .'%',
//        formater($marge, 2) .'€',
//        formater($prix_vente, 2) .'€',
//        formater($prix_par_boulon, 2) .'€'
//        );
    
    $prix_ventes = array();
    $prix_ventes[] = array(
        "Coût de l'usine",
        "Coût des bobines",
        "Coût de revient",
        "Bénéfice",
        "Taux de marge réel"
        );
    $prix_ventes[] = array(formater($cout_usine, 2) .'€',
        formater($cout_bobine, 2) .'€',
        formater($cout_revient, 2) .'€',
        formater($benefice, 2) .'€',
        formater(($taux_marge_reel * 100), 2) .'%'
        );
    
    return array('resultat' => $prix_ventes, 
        'parametre' => $params,
        'machines' => $machines,
        'contrat' => $affiche_commandes,
        'commandes_bobine' => $commande_bobine,
        'production' => $resultat,
        'expedition' => $affiche_commande_expedie,
        'projet' => $affiche_projet,
        'gain' => $affiche_gain_par_projet);
}

// Affiche un tableau PHP à 2 dimensions sous forme HTML
function affichage_tableau($tableau)
{
    $first = true;
    $nb_cols_first = 0;
    $nb_cols = 0;
    echo '<table class="table table-striped">';
    foreach ($tableau as $ligne)
    {
        if($first)
        {
            $id_col = 'th';
            $first = false;
            $nb_cols_first = count($ligne);
        }
        else
        {
            $id_col = 'td';
        }
        $nb_cols = count($ligne);
        
        if($nb_cols_first != $nb_cols)
        {
            if(isset($ligne[0]))
            {
                $class = $ligne[0];
            }
        }
        else
        {
            $class = '';
        }
        $i = $nb_cols;
        echo '<tr class="row '. $class .'">';
        foreach ($ligne as $colonne)
        {
            if($nb_cols_first == $nb_cols or $i <= $nb_cols_first)
            {
                echo '<'.$id_col.'>'. $colonne .'</'.$id_col.'>';
            }
            $i--;
        }
        echo '</tr>';
    }
    echo '</table>';
}

function formater($nombre, $decimal = 0)
{
    return number_format($nombre, $decimal, ',', ' ');
}

// Retourne vrai si le jour est ouvré, faux sinon
function estOuvre($timestamp)
{
    $res = false;
    // Elimine les Samedi (6) et Dimanche (0)
    if((date('w',$timestamp)!=0)&&(date('w',$timestamp)!=6))
    {
        $res = true;
    }
    return $res;
}

// Time d'une date au format "dd-mm-yyyy"
function getTimeOfDate($date)
{
    $date_explose = explode('-',$date);
    $time = mktime(0, 0, 0, $date_explose[1], $date_explose[0], $date_explose[2]);
    return $time;
}

// Renvoi le timestamp au format date
function getDateOfTime($time)
{
    return date("d-m-Y", $time);
}

// Retourne le nombre de jour ouvré (Lundi à Vendredi)
// Date au format "dd-mm-yyyy"
function getJoursOuvre($datedeb, $datefin)
{
    $nb_jours = 0;
    $timestampcurr = getTimeOfDate($datedeb);
    $timestampf = getTimeOfDate($datefin);
    while($timestampcurr<$timestampf)
    {
        if(estOuvre($timestampcurr))
        {
            $nb_jours++;
        }
        
        $timestampcurr = mktime(0, 0, 0, date('m', $timestampcurr), (date('d', $timestampcurr)+1), date('Y', $timestampcurr));
    }
    
    return $nb_jours;
}
?>